Skip to content

Fix #15217 (gtk.cfg: g_idle_add and g_timeout_add take the data pointer) - #9008

Merged
danmar merged 3 commits into
cppcheck-opensource:mainfrom
pduchi:gtk-cfg-glib-source-data-ownership
Nov 13, 2026
Merged

danmar merged 3 commits into
cppcheck-opensource:mainfrom
pduchi:gtk-cfg-glib-source-data-ownership

Conversation

@pduchi

@pduchi pduchi commented Nov 9, 2026 •

Copy link
Copy Markdown
Contributor

I don't have a Trac account, so there is no ticket for this; the details are below. Feel free to create one and I'll add it to the title.

#include <glib.h>

static gboolean free_data(gpointer data)
{
    g_free(data);
    return FALSE;
}

void f(void)
{
    gchar *data = g_strdup("data");
    g_timeout_add(10, free_data, data);
}

$ cppcheck --library=gtk idle.c
idle.c:13:1: error: Memory leak: data [memleak]

GLib passes the data to the callback, and to the destroy notify of the _full variants, which commonly frees it, so there is no leak. gtk.cfg marked these functions <leak-ignore/>:

  • g_idle_add, g_idle_add_full
  • g_timeout_add, g_timeout_add_seconds, g_timeout_add_full, g_timeout_add_seconds_full

They are now <use> in the g_free memory group, without <leak-ignore/>. Their only pointer arguments are the callback, the data and the destroy notify, so <use> can't hide another leak. Their data argument also loses direction="in", which made cppcheck suggest a pointer to const for the data.

This is the gtk.cfg change that #8953 (#15137) was made for: without that fix, --check-library still reported checkLibraryUseIgnore for these functions. Here <use> means what the reference describes: the function takes over the pointer.

Known limitation: a <use> function doesn't get the deallocuse check, so g_free(p); g_idle_add(cb, p); is no longer reported. test/cfg/gtk.c records this as a TODO suppression.

Tests: test/cfg/gtk.c covers all six functions; the memleak false positives show without the change. cfg/gtk.cfg validates against cppcheck-cfg.rng.

The data given to g_idle_add(), g_timeout_add() and their _seconds/_full
variants is passed to the callback (and the destroy notify), which
usually frees it. With <leak-ignore/> every such call was reported as
  Memory leak: data
Configure them as <use> instead.

@dmcppcheck dmcppcheck left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI review

This PR causes a new false negative: leaks of a GMainContext passed to g_main_context_invoke / g_main_context_invoke_full are no longer reported. CheckLeakAutoVarImpl::functionCall applies a <use> function to every plain pointer variable in the call, because <use> has no argument number. So g_main_context_invoke(ctx, cb, NULL) now marks the context in ctx as taken over. That context comes from g_main_context_new(), which is in the g_main_context_unref group. GLib does not take ownership of the context; it only refs it internally. The base config reports Memory leak: ctx for this code; the PR does not. g_idle_add* and g_timeout_add* don't have this problem, because their only pointer parameters are the callback, the data and the destroy notify.

The memleak false positives that the PR is about are gone, and test/cfg/gtk.c passes. The new <arg> configuration for g_main_context_invoke* works: it reports a NULL callback as nullPointer and an uninitialized callback or priority as uninitvar.

Testing

Build: make -j8 cppcheck testrunner in pr/ succeeded.

cfg test: I ran test/cfg/gtk.c with the runtests.sh options (--check-library --platform=unix64 --enable=style,information --inconclusive --force --check-level=exhaustive --inline-suppr ... --library=gtk).

  • With the PR's gtk.cfg: clean, exit code 0.
  • With the base gtk.cfg: 8 memleak errors in the new test functions. So the test does cover the change.

Regression found (I compared the PR's cfg with a copy of base's gtk.cfg, both run with the PR binary, since only the cfg changed):

static gboolean cb(gpointer data) { g_free(data); return FALSE; }
void ctx_leak(void)
{
    GMainContext *ctx = g_main_context_new();
    g_main_context_invoke(ctx, cb, NULL);
}
void ctx_ok(void)
{
    GMainContext *ctx = g_main_context_new();
    g_main_context_invoke(ctx, cb, NULL);
    g_main_context_unref(ctx);
}
$ cppcheck -q --library=/tmp/b/gtkbase.cfg t2.c
t2.c:16:1: error: Memory leak: ctx [memleak]
$ cppcheck -q --library=pr/cfg/gtk.cfg t2.c
(no output)

g_main_context_invoke_full(ctx, 0, cb, NULL, NULL) behaves the same way: base reports the leak, the PR does not.

Other checks:

  • g_main_context_invoke(NULL, NULL, NULL) now gives nullPointer, and an uninitialized callback or priority gives uninitvar.
  • A malloc'd or g_new'd pointer passed as the data is no longer reported, and no mismatch is reported either.
  • A conditional if (x) g_idle_add(cb, p); is no longer reported. That matches how if (x) g_free(p); is already handled, so I don't count it as a problem of this PR.

Automated review of 51b97a0 by Claude. The pull request was built and tested locally.

Comment thread cfg/gtk.cfg Outdated
<use>g_register_data</use>
<use>g_idle_add</use>
<use>g_idle_add_full</use>
<use>g_main_context_invoke</use>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI review

<use> has no argument number. CheckLeakAutoVarImpl::functionCall treats every plain pointer variable in a non-leak-ignore call as taken over, and that includes the GMainContext *context argument. As a result, this leak is no longer found (base reports Memory leak: ctx, the PR reports nothing):

void f(void) {
    GMainContext *ctx = g_main_context_new();
    g_main_context_invoke(ctx, cb, NULL);
}

The same happens with g_main_context_invoke_full. GLib does not take ownership of the context. Consider adding a test/cfg/gtk.c case with // cppcheck-suppress memleak for this, and fixing it in the library or check, for example with a per-argument <use>/ownership setting. Otherwise, consider leaving the g_main_context_invoke* functions out of this change.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, you're right: applies to every pointer argument, so g_main_context_invoke(ctx, ...) hid a leak of ctx. I dropped g_main_context_invoke and g_main_context_invoke_full from this PR; they keep for now.

@pduchi
pduchi force-pushed the gtk-cfg-glib-source-data-ownership branch from 51b97a0 to 46f1427 Compare November 10, 2026 10:06
@pduchi pduchi changed the title gtk.cfg: g_idle_add, g_timeout_add and g_main_context_invoke take the data pointer gtk.cfg: g_idle_add and g_timeout_add take the data pointer Nov 10, 2026

@dmcppcheck dmcppcheck left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI review

The earlier problem is fixed: the PR no longer touches g_main_context_invoke*, so a leaked GMainContext is reported again. The memleak false positives that the PR is about are gone, and test/cfg/gtk.c passes.

New finding (minor false negative): removing <leak-ignore/> also removes the use-after-free check for these calls. With base, g_free(p); g_idle_add(cb, p); gives deallocuse. With the PR it gives nothing. In CheckLeakAutoVarImpl::functionCall, only leak-ignore functions go through checkTokenInsideExpression; a <use> function only changes the pointer's alloc status. Every <use> function behaves this way, so this is a trade-off of the approach rather than a bug in the cfg. It could still be pinned down with a test in test/cfg/gtk.c. I found no other problems.

Earlier comments

# Comment By Where Status
1 on g_main_context_invoke* hides leaks of the GMainContext argument AI review cfg/gtk.cfg:722 ❔ Code removed

Testing

Build: make -j8 cppcheck in pr/ succeeded. Only the cfg changed, so I compared the PR's cfg/gtk.cfg with a copy of base's gtk.cfg, both run with the PR binary.

cfg test: I ran test/cfg/gtk.c with the runtests.sh options (--check-library --inline-suppr --error-exitcode=1 ... --library=gtk). It was clean, exit code 0.

Scratch test (scratch/t.c):

#include <glib.h>
#include <stdlib.h>
static gboolean cb(gpointer d){g_free(d);return FALSE;}
void a(void){ gchar *p=g_strdup("x"); g_idle_add(cb,p); g_free(p); }
void b(void){ gchar *p=g_strdup("x"); g_free(p); g_idle_add(cb,p); }
void c(void){ char *p=malloc(3); g_idle_add(cb,p); free(p); }
void d(void){ gchar *p=g_strdup("x"); gchar *q=g_strdup("y"); g_timeout_add(1,cb,p); }
void e(int x){ gchar *p=g_strdup("x"); if(x) return; g_idle_add(cb,p); }
void f(void){ g_idle_add(cb,g_strdup("x")); }
void g(void){ GMainContext *ctx=g_main_context_new(); g_main_context_invoke(ctx,cb,NULL);}
void h(void){ gchar *p=g_strdup("x"); g_idle_add_full(0,cb,p,g_free); g_free(p);}
$ cppcheck -q --library=gtkbase.cfg --template='{line}:{id}:{message}' t.c
5:deallocuse:Dereferencing 'p' after it is deallocated / released
7:memleak:Memory leak: p
7:memleak:Memory leak: q
8:memleak:Memory leak: p
10:memleak:Memory leak: ctx
9:leakNoVarFunctionCall:Allocation with g_strdup, g_idle_add doesn't release it.
$ cppcheck -q --library=pr/cfg/gtk.cfg --template='{line}:{id}:{message}' t.c
7:memleak:Memory leak: q
8:memleak:Memory leak: p
10:memleak:Memory leak: ctx
  • Lines 7, 8 and 9: the PR removes the intended false positives. The real leaks (q, and the early-return path) are still reported.
  • Line 10: the GMainContext leak from the earlier review is reported again.
  • Line 5: the deallocuse warning (use after free) is lost, which is the new false negative.
  • Lines 4 and 11: the double free through the callback is not reported, with base or with the PR.

Automated review of 46f1427 by Claude. The pull request was built and tested locally.

Comment thread cfg/gtk.cfg
<alloc init="true" no-fail="true">g_variant_type_dup_string</alloc>
<alloc init="true" no-fail="true">g_value_dup_string</alloc>
<use>g_register_data</use>
<use>g_idle_add</use>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI review

Taking these functions off <leak-ignore/> also turns off the use-after-free check for their arguments. CheckLeakAutoVarImpl::functionCall calls checkTokenInsideExpression only for leak-ignore functions; a <use> function only gets changeAllocStatus. As a result:

void b(void){ gchar *p=g_strdup("x"); g_free(p); g_idle_add(cb,p); }

Base reports deallocuse: Dereferencing 'p' after it is deallocated / released. With this PR nothing is reported. This is how <use> works in general, so it may be acceptable, but it is a new false negative for these six functions.

direction="in" on the gpointer data argument made cppcheck suggest a
pointer to const (constVariablePointer) for the data, although the
function hands it to the callback and takes a non-const gpointer.
g_idle_add_full already had no direction on it.
A <use> function does not get the deallocuse check that a <leak-ignore/>
function gets, so passing freed data to g_idle_add() is no longer
reported. Record it as a TODO suppression.

@dmcppcheck dmcppcheck left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI review

I found no new problems. The two issues raised earlier are dealt with:

  • The false negative for a GMainContext leak through g_main_context_invoke* is fixed, because those functions were dropped from the PR.
  • The lost deallocuse check for these six functions is still there. It is a limitation of <use> itself, and the PR now records it as a TODO suppression in test/cfg/gtk.c.

The memleak false positives the PR targets are gone. Real leaks of other variables are still reported.

Earlier comments

# Comment By Where Status
1 on g_main_context_invoke* hides leaks of the GMainContext argument AI review cfg/gtk.cfg:722 ✅ Fixed
2 Removing loses the deallocuse check for g_idle_add/g_timeout_add data AI review cfg/gtk.cfg:720 💬 Answered: Accepted as a known limitation of , recorded as a TODO suppression in test/cfg/gtk.c

Testing

Build: make -j8 cppcheck in pr/ succeeded.

cfg test: I ran test/cfg/gtk.c with the PR binary and --library=gtk, using the runtests.sh style options including --debug-warnings and --inline-suppr. The only messages were unmatched suppressions for the extra --suppress options that I had passed myself. The test file gave no warnings.

Scratch comparison: I ran the PR binary on the same file twice, once with base's gtk.cfg and once with the PR's gtk.cfg:

static gboolean cb(gpointer d){return FALSE;}
void a(void){ GHashTable *h = g_hash_table_new(NULL,NULL); g_idle_add(cb,h); }
void b(void){ GObject *o = g_object_new(0,NULL); g_timeout_add(1,cb,o); }
void c(void){ GList *l = g_list_alloc(); g_idle_add(cb,l); }
void d(void){ GString *s = g_string_new("x"); g_idle_add(cb,s); }
void e(void){ gchar *p = g_strdup("x"); g_idle_add(cb,p); g_free(p); }
void f(void){ gchar *p = g_strdup("x"); g_idle_add(cb,p); p[0]=0; }
void g(void){ gchar *p = g_strdup("x"); guint id = g_idle_add(cb,p); (void)id; }
void h(void){ GHashTable *h = g_hash_table_new(NULL,NULL); g_idle_add(cb,h); g_hash_table_unref(h);}
  • Base cfg: memleak in a, b, c, d, f and g, plus constVariablePointer for the data variables.
  • PR cfg: none of these. Only the unrelated constParameterCallback remains.

Data from other allocator groups (hash table, GObject, GList, GString) is now treated as handed over. I saw no new mismatch or double-free false positives.


Automated review of c10a655 by Claude. The pull request was built and tested locally.

@danmar danmar changed the title gtk.cfg: g_idle_add and g_timeout_add take the data pointer Fix #15217 (gtk.cfg: g_idle_add and g_timeout_add take the data pointer) Nov 13, 2026
@danmar
danmar merged commit ada0dfd into cppcheck-opensource:main Nov 13, 2026
71 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants